package com.tyros.storage.tool;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 文件分片合并工具类
 *
 * @author zhangshuaiyin
 * @date 2022/3/11 15:20
 */
public class FileMergeTools {

    /**
     * 合并文件
     *
     * @param partDirPath    拆分文件所在目录名
     * @param partFileSuffix 拆分文件后缀名
     * @param partSize       拆分文件的字节数大小
     * @param mergedFilePath 合并后的文件路径
     */
    public static void merge(String partDirPath, String partFileSuffix, int partSize, String mergedFilePath) {
        ArrayList<File> partFiles = getDirFiles(partDirPath, partFileSuffix);
        partFiles.sort(new FileComparator());

        try (RandomAccessFile randomAccessFile = new RandomAccessFile(mergedFilePath, "rw")) {
            randomAccessFile.setLength((long) partSize * (partFiles.size() - 1) + partFiles.get(partFiles.size() - 1).length());
        } catch (IOException e) {
            e.printStackTrace();
        }

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                partFiles.size(),
                partFiles.size() * 3,
                1,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(partFiles.size() * 2),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < partFiles.size(); i++) {
            threadPool.execute(new MergeRunnable((long) i * partSize, mergedFilePath, partFiles.get(i)));
        }
        threadPool.shutdown();
    }

    /**
     * 获取指定目录下特定文件后缀名的文件列表(不包括子文件夹)
     *
     * @param dirPath 目录路径
     * @param suffix  文件后缀
     * @return 文件列表
     */
    public static ArrayList<File> getDirFiles(String dirPath, final String suffix) {
        File path = new File(dirPath);

        File[] fileArr = path.listFiles((dir, name) -> {
            String lowerName = name.toLowerCase();
            String lowerSuffix = suffix.toLowerCase();
            return lowerName.endsWith(lowerSuffix);
        });

        ArrayList<File> files = new ArrayList<>();
        if (fileArr != null) {
            for (File f : fileArr) {
                if (f.isFile()) {
                    files.add(f);
                }
            }
        }
        return files;
    }

    /**
     * 根据文件名，比较文件
     */
    public static class FileComparator implements Comparator<File> {
        @Override
        public int compare(File o1, File o2) {
            return o1.getName().compareToIgnoreCase(o2.getName());
        }
    }

    /**
     * 合并处理 Runnable
     */
    private static class MergeRunnable implements Runnable {
        long startPos;
        String mergedFilePath;
        File partFile;

        public MergeRunnable(long startPos, String mergedFilePath, File partFile) {
            this.startPos = startPos;
            this.mergedFilePath = mergedFilePath;
            this.partFile = partFile;
        }

        @Override
        public void run() {
            try (RandomAccessFile rFile = new RandomAccessFile(mergedFilePath, "rw");
                 FileInputStream fis = new FileInputStream(partFile)) {
                rFile.seek(startPos);
                byte[] b = new byte[fis.available()];
                fis.read(b);
                rFile.write(b);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        FileMergeTools.merge(FileSplitTools.PART_DIR, FileSplitTools.FILE_TYPE_PART, 100 * 1024, FileSplitTools.PART_DIR + "test.jpg");
    }
}
